/**
  ******************************************************************************
  * @file    PSU_Fault_Processing.c
  * @author  STMicroelectronics
  * @version 1.1.0
  * @date    09-Nov-2023
  * @brief   DC/DC fault management
  *          This file provides fuctions to check DC/DC's faults and related thresholds
  ******************************************************************************
  *
  * Copyright (c) 2022 Biricha Digital Power Limited
  * Copyright (c) 2014(-2023) STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file in
  * the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  ******************************************************************************
  *
  */

/** @addtogroup STM32_DPOW
  * @brief  Digital Power application implemented with a STM32 MCU
  * @{
  */

/** @addtogroup Fault_Processing
  * @{
  */

/* Includes ------------------------------------------------------------------*/
#include "dcdc_fault_processing.h"
#include "app_X-CUBE-DPower.h"
#include "dpwr_pfc.h"
#include "DPC_Lib_Conf.h"
#include "DCDC_Control_Layer.h"
#include "DCDC_Globals.h"
#include "stm32g4xx_hal.h"
#include "stm32_hal_legacy.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define PRIMARY_STATUS_ERROR_MSK        ((uint8_t)(0xFE)) /**< bit mask for satus sent by primary MCU */
#define OVER_TEMP_FAULT_NOT_RECOVERABLE                   /**< if defined the over-temperature fault is not recoverable (the board must be powered off) */
#define OVER_VOLTAGE_OUT_FAULT_NOT_RECOVERABLE            /**< if defined the output over-voltage fault is not recoverable (the board must be powered off) */
#define UNDER_VOLTAGE_OUT_FAULT_NOT_RECOVERABLE           /**< if defined the output under-voltage fault is not recoverable (the board must be powered off) */
#define SHORT_CIRCUIT_VALIDATION_TIME_mS        30        /**< validation time in ms for undervoltage fault */

/* ----- Fault and burst mode thresholds ----- */
/* Output Voltage thresholds */
#ifdef LOW_VOLTAGE
#define OUT_VOLTAGE_MAX_H               OUT_VOLT_ADC_VALUE(6)                              /**< max voltage for overvoltage fault detection [V] */
#define OUT_VOLTAGE_HYSTERESIS          OUT_VOLT_ADC_VALUE(1)                              /**< hysteresis for under/over voltage fault detection [V] */
#define OUT_VOLTAGE_MIN_L               OUT_VOLT_ADC_VALUE(30/10)          /**< min voltage for undervoltage fault detection [V] */
#else
#define OUT_VOLTAGE_MAX_H               OUT_VOLT_ADC_VALUE(56)                             /**< max voltage for overvoltage fault detection [V] */
#define OUT_VOLTAGE_HYSTERESIS          OUT_VOLT_ADC_VALUE(4)                              /**< hysteresis for under/over voltage fault detection [V] */
#define OUT_VOLTAGE_MIN_L               OUT_VOLT_ADC_VALUE(30)             /**< min voltage for undervoltage fault detection [V] */
#endif
#define OUT_VOLTAGE_MAX_L               (OUT_VOLTAGE_MAX_H - OUT_VOLTAGE_HYSTERESIS)       /**< min voltage for overvoltage fault cancellation [V] */
#define OUT_VOLTAGE_MIN_H               (OUT_VOLTAGE_MIN_L + OUT_VOLTAGE_HYSTERESIS)       /**< max voltage for undervoltage fault cancellation [V] */

/* Burst mode on bus voltage thresholds */
#ifdef LOW_VOLTAGE
#define OUT_VOLTAGE_BURST_MODE_TH_H     OUT_VOLT_ADC_VALUE(4)                              /**< max voltage for burst mode activation [V] */
#else
#define OUT_VOLTAGE_BURST_MODE_TH_H     OUT_VOLT_ADC_VALUE(50)                             /**< max voltage for burst mode activation [V] */
#endif
#define OUT_VOLTAGE_BURST_MODE_TH_L     NOMINAL_OUT_VOLTAGE                                /**< min voltage for burst mode de-activation [V] */

/* Temperature thresholds */
#define TEMPERATURE_TH_H                ((uint16_t)(100))                                  /**< max temperature for over-temperature fault detection (higher threshold) [�C] */
#define TEMPERATURE_HYSTERESIS          ((uint16_t)(5))                                    /**< hysteresis for over-temperature fault detection [�C] */
#define TEMPERATURE_TH_L                (TEMPERATURE_TH_H - TEMPERATURE_HYSTERESIS)        /**< min temperature for over-temperature fault cancellation [�C] */

/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/

/* Private function prototypes -----------------------------------------------*/
#if (OVERCURRENT_PROTECTION == true)
/**
  * @brief  This function checks if an overcurrent is passed
  * @param  None
  * @retval None
  * @details
  * When an overcurrent condition is passed,
  * the DCDC_System_Fault is updated and FAULT status is reset, it is set in ISR.
  */
static void DCDC_OverCurrentCheck(void)
{
}
#endif

#if (OVERTEMPERATURE_PROTECTION == true)
/**
  * @brief  This function checks if an overtemperature has occurred
  * @param  None
  * @retval None
  * @details
  * When an overtemperature condition is present,
  * the DCDC_System_Fault is updated and FAULT status is set.
  */
static void DCDC_TemperatureCheck(void)
{
  /* Overtemperature DCDC protection */
  if ( (fTemperatureDeg > TEMPERATURE_TH_H) ||
       ((fTemperatureDeg > TEMPERATURE_TH_L) && ((myDcdc.m_DCDC_System_Fault & DCDC_OVER_TEMP) == DCDC_OVER_TEMP)) )
  {
    myDcdc.m_DCDC_System_Fault |= DCDC_OVER_TEMP;
    /* Update LED blinking if last error is changed */
    if ( (LED_LastFaultShown != DCDC_OVER_TEMP) && (bFirstFaultDetected == false) )
    {
      /* Store last fault shown on LED */
      LED_LastFaultShown = DCDC_OVER_TEMP;
    }
    bFirstFaultDetected = true;
    myDcdc.m_DCDC_State = DCDC_STOP;
  }
  #ifndef OVER_TEMP_FAULT_NOT_RECOVERABLE
  else
  {
    // Clear DCDC over temperature flag
    myDcdc.m_DCDC_System_Fault &= ~DCDC_OVER_TEMP;
    myDcdc.m_DCDC_State = DCDC_ERROR;
  }
  #endif
}
#endif

/**
  * @brief  This function checks if there is an overvoltage on Viper supply - NOT USED
  * @param  None
  * @retval None
  * @details
  * When a overvoltage is present, the "myDcdc.m_DCDC_System_Fault is updated and FAULT status is set
  */
void DCDC_ViperVoltageCheck(void)
{
  // insert here the filter and the control on Viper Voltage if required
}

#if ( (OVERVOLTAGE_PROTECTION == true) || (SHORT_CIRCUIT_PROTECTION == true) )
/**
  * @brief  This function checks if an over/under voltage on DC bus has occurred
  * @param  None
  * @retval None
  * @details
  * When an over/under voltage on DC bus condition is present,
  * the DCDC_System_Fault is updated and burst mode or FAULT status is set.
  */
static void DCDC_OutVoltageCheck(void)
{
  #if (SHORT_CIRCUIT_PROTECTION == true)
  static bool bFirstUndervoltageCheck = true;
  #endif

  /* Out_Voltage filtered before Voltage Control loop */
  hVoutVoltageFiltered = ((hVoutVoltageFiltered + (DCDC_MeasureStruct.hVout)) >> 1);

  #if (OVERVOLTAGE_PROTECTION == true)
  /* Overvoltage OUT_H protection */
  if ( (hVoutVoltageFiltered > OUT_VOLTAGE_MAX_H) ||
       ((hVoutVoltageFiltered > OUT_VOLTAGE_MAX_L) && ((myDcdc.m_DCDC_System_Fault & DCDC_OUT_OVER_VOLT) == DCDC_OUT_OVER_VOLT)) )
  {
    myDcdc.m_DCDC_System_Fault |= DCDC_OUT_OVER_VOLT;

    /* Update LED blinking if last error is changed */
    if ((LED_LastFaultShown != DCDC_OUT_OVER_VOLT) && (bFirstFaultDetected == false))
    {
      /* Store last fault shown on LED */
      LED_LastFaultShown = DCDC_OUT_OVER_VOLT;
    }
    bFirstFaultDetected = true;
    myDcdc.m_DCDC_State = DCDC_STOP;
  }
  #endif

  #ifndef OVER_VOLTAGE_OUT_FAULT_NOT_RECOVERABLE
  else
  {
    myDcdc.m_DCDC_System_Fault &= ~DCDC_OUT_OVER_VOLT;
    myDcdc.m_DCDC_State = DCDC_ERROR;
  }
  #endif

  #if (OUT_VOLTAGE_BURST_MODE == true)
  if ( ((hVoutVoltageFiltered > OUT_VOLTAGE_BURST_MODE_TH_H) ||
       ((hVoutVoltageFiltered > OUT_VOLTAGE_BURST_MODE_TH_L))) && (bVoutBurstModeEnabled == true) )
  {
    if ( bVoutBurstModeEnabled == false )
    {
      /* Enable burst mode */
      HAL_HRTIM_BurstModeCtl(&DPC_APPL_HRTIM, HRTIM_BURSTMODECTL_ENABLED);
      /* Set burst mode variable */
      bVoutBurstModeEnabled = true;
    }
  }
  else
  {     // within the correct boundaries
    if ( bVoutBurstModeEnabled == true )
    {
      /* Disable burst mode */
      HAL_HRTIM_BurstModeCtl(&DPC_APPL_HRTIM, HRTIM_BURSTMODECTL_DISABLED);
      /* Reset burst mode variable */
      bVoutBurstModeEnabled = false;
    }
  }
  #endif

  #if (SHORT_CIRCUIT_PROTECTION == true)
  // insert a number of iteration for short circuit validation ?
  /* Undervoltage OUT protection */
  if ( bCheckUndervoltageEnabled == true )
  {
    bFirstUndervoltageCheck = true;

    /* Vbus undervoltage */
    if ( (hVoutVoltageFiltered < OUT_VOLTAGE_MIN_L) ||
         ((hVoutVoltageFiltered < OUT_VOLTAGE_MIN_H) && ((myDcdc.m_DCDC_System_Fault & DCDC_SHORT_CIRCUIT) == DCDC_SHORT_CIRCUIT)) )
    {
      if ( bFirstUndervoltageCheck == true )
      {
        /* Set delay time to validate undervoltage protection */
        SetDelayTime(&hDCDC_UndervoltageValidation_TimeLeft, SHORT_CIRCUIT_VALIDATION_TIME_mS);
        bFirstUndervoltageCheck = false;
      }
      else
      {
        if ( DelayTimeIsElapsed(&hDCDC_UndervoltageValidation_TimeLeft) )
        {
          myDcdc.m_DCDC_System_Fault |= DCDC_SHORT_CIRCUIT;
          /* Update LED blinking if last error is changed */
          if ( (LED_LastFaultShown != DCDC_SHORT_CIRCUIT) && (bFirstFaultDetected == false) )
          {
            /* store last fault shown on LED */
            LED_LastFaultShown = DCDC_SHORT_CIRCUIT;
          }
          bFirstFaultDetected = true;
          myDcdc.m_DCDC_State = DCDC_STOP;
        }
      }
    }
    else
    {
      bFirstUndervoltageCheck = true;
      #ifndef UNDER_VOLTAGE_OUT_FAULT_NOT_RECOVERABLE
      myDcdc.m_DCDC_State = DCDC_ERROR;
      myDcdc.m_DCDC_System_Fault &= ~DCDC_SHORT_CIRCUIT;
      #endif
    }
  }
  #endif
}
#endif

/* External variables --------------------------------------------------------*/
/* External functions ---------------------------------------------------------*/
/**
  * @brief  This function checks if a fault has occured
  * @param  None
  * @retval None
  *
  *  this function calls \a DCDC_TemperatureCheck(), \a DCDC_PrimaryStatusCheck(), \a DCDC_OverCurrentCheck() and \a DCDC_OutVoltageCheck()
  */
void DCDC_FaultCheck(void)
{
  /* Reset first fault detected flag */
  bFirstFaultDetected = false;

  #if (OVERCURRENT_PROTECTION == true)
  /* Check if an overcurrent is passed - if commented, only a reset of the system can reenable the Power Supply */
  DCDC_OverCurrentCheck();
  #endif

  #if ((OVERVOLTAGE_PROTECTION == true) || (SHORT_CIRCUIT_PROTECTION == true))
  /* Check if an under/over output voltage is passed */
  DCDC_OutVoltageCheck();
  #endif

  /* Update previous fault variable */
  myDcdc.m_DCDC_Previous_System_Fault = myDcdc.m_DCDC_System_Fault;
}

/**
  * @} end Fault_Processing
  */

/**
  * @} end STM32_DPOW
  */
